package jp.ac.waseda.cs.washi.game.entity;

import java.awt.Image;

import jp.ac.waseda.cs.washi.game.MagicManager;
import jp.ac.waseda.cs.washi.game.MoveManager;
import jp.ac.waseda.cs.washi.game.Settings;
import jp.ac.waseda.cs.washi.game.api.CharaInfo;
import jp.ac.waseda.cs.washi.gameaiarena.api.Direction4;
import jp.ac.waseda.cs.washi.game.api.MapInfo;
import jp.ac.waseda.cs.washi.gameaiarena.api.Point2;
import jp.ac.waseda.cs.washi.gameaiarena.common.AbstractMap;
import jp.ac.waseda.cs.washi.gameaiarena.gui.Renderer;

public class Map extends AbstractMap<Tile> {
	class MapInfoImpl implements MapInfo {
		@Override
		public int getHeight() {
			return Map.this.getHeight();
		}

		@Override
		public int getWidth() {
			return Map.this.getWidth();
		}

		@Override
		public boolean isAvailable(int x, int y) {
			return Map.this.isAvailable(x, y);
		}

		@Override
		public boolean isAvailable(Point2 location) {
			return Map.this.isAvailable(location);
		}

		@Override
		public boolean isMovable(CharaInfo charaInfo, Direction4 direction) {
			return MoveManager.isMovable(this, charaInfo, direction);
		}

		@Override
		public boolean isMovable(Point2 location) {
			return MoveManager.isMovable(this, location);
		}

		@Override
		public boolean isMovable(int x, int y) {
			return isMovable(new Point2(x, y));
		}

		@Override
		public Direction4[] getPath(Point2 from, Point2 to) {
			return PathSearch.Search(this, from, to);
		}
		
		@Override
		public boolean isCompleted(int x, int y) {
			Tile tile = Map.this.getTile(x, y);
			return tile.getCompleteOwner() != null; 
		}
		
		@Override
		public boolean isCompleted(Point2 p) {
			return isCompleted(p.x, p.y); 
		}

		@Override
		public CharaInfo getOwner(int x, int y) {
			Tile tile = Map.this.getTile(x, y);
			if (tile == null)
				return null;
			Chara owner = tile.getOwner();
			if (owner == null)
				return null;
			return owner.getInfo();
		}

		@Override
		public CharaInfo getOwner(Point2 p) {
			return getOwner(p.x, p.y);
		}
		
		@Override
		public CharaInfo getCompleteOwner(int x, int y) {
			Tile tile = Map.this.getTile(x, y);
			if (tile == null)
				return null;
			Chara owner = tile.getCompleteOwner();
			if (owner == null)
				return null;
			return owner.getInfo();
		}

		@Override
		public CharaInfo getCompleteOwner(Point2 p) {
			return getCompleteOwner(p.x, p.y);
		}

		@Override
		public int getRequiredDevelopCount(CharaInfo charaInfo, int x, int y) {
			Point2 p = new Point2(x, y);
			Tile t = Map.this.getTile(p);
			CharaInfo owner = getCompleteOwner(p);
			if (t == null || t.hasWall() || !t.canDevelop() || owner == charaInfo)
				return 0;
			int solidCount = Settings.COMPLETE_STRENGTH / Settings.DEVELOP_STRENGTH;
			if (t.getOwner() == null)
				return solidCount;
			if (t.getOwner().getInfo() == charaInfo)
				return (Settings.COMPLETE_STRENGTH - getStrength(p) + Settings.DEVELOP_STRENGTH - 1) / Settings.DEVELOP_STRENGTH;

			Chara enemy = t.getCompleteOwner();
			if (enemy != null) {
				PumpkinCore core = MagicManager.getPumpkinCore(enemy, p);
				if (core != null) {
					return solidCount + core.getStrength() / Settings.DESTROY_STRENGTH;
				}
			}
			return solidCount + t.getStrength() / Settings.DESTROY_STRENGTH;
		}

		@Override
		public int getRequiredDevelopCount(CharaInfo charaInfo, Point2 p) {
			return getRequiredDevelopCount(charaInfo, p.x, p.y);
		}

		@Override
		public boolean isWall(int x, int y) {
			Tile t = getTile(x, y);
			return t.hasWall();
		}

		@Override
		public boolean isWall(Point2 p) {
			return isWall(p.x, p.y);
		}

		@Override
		public boolean canDevelop(int x, int y) {
			Tile t = getTile(x, y);
			return t.canDevelop();
		}

		@Override
		public boolean canDevelop(Point2 p) {
			return canDevelop(p.x, p.y);
		}

		@Override
		public int getStrength(int x, int y) {
			Tile t = getTile(x, y);
			return t.getStrength();
		}

		@Override
		public int getStrength(Point2 p) {
			return getStrength(p.x, p.y);
		}
	}

	// ----- ----- ----- ----- fields ----- ----- ----- -----
	private final int _width, _height;
	private final MapInfo _mapInfo;

	public static final int BLOCK_WIDTH = 32;
	public static final int BLOCK_HEIGHT = 32;

	// ----- ----- ----- ----- ctor ----- ----- ----- -----
	public Map(int width, int height, Image backgroundImage) {
		assert width > 0 && height > 0;
		_width = width;
		_height = height;
		for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++)
				setTile(x, y, new Tile(backgroundImage));
		_mapInfo = new MapInfoImpl();
	}

	// ----- ----- ----- ----- methods ----- ----- ----- -----

	// ----- ----- ----- public ----- ----- -----
	public MapInfo getInfo() {
		return _mapInfo;
	}

	public int getWidth() {
		return _width;
	}

	public int getHeight() {
		return _height;
	}

	public void draw(Renderer render) {
		for (int x = 0; x < _width; x++) {
			for (int y = 0; y < _height; y++) {
				Tile tile = getTile(x, y);
				tile.draw(render, x, y);
			}
		}
	}
}
